#!/bin/bash -e

### @accetto, December 2020
### https://github.com/accetto/ubuntu-vnc-xfce-g3
### https://github.com/accetto/ubuntu-vnc-xfce-g3/wiki

# Created by argbash-init v2.8.1
# ARG_VERSION([echo $0 v21.07.31])
# ARG_HELP([Utility for previewing README files and publishing them on Docker Hub. Assumes to be executed from the current directory.])
#
# ARG_POSITIONAL_SINGLE([command],[Command to execute. Available commands: \n \
# cleanup - Deletes the previously generated README preview and other helper files. \n \
#           This is the default command if both <command> and <repo> are empty. \n \
# preview - Generates the README preview file and checks its length. \n \
#           This is the default command if <command> is empty, but <repo> is not. \n \
# publish - Generates the README file and publishes it to Docker Hub. \n \
#           This command must be requested explicitely. \n \
#],[""])
#
# ARG_DEFAULTS_POS([])
# ARG_POSITIONAL_DOUBLEDASH([])
#
# ARG_OPTIONAL_SINGLE([repo],[],[Docker Hub repository to publish to (owner/repo)],[""])
# ARG_OPTIONAL_SINGLE([context],[],[Path to the readme files (relative to PWD, e.g. '../docker/xfce')],[""])
# ARG_OPTIONAL_SINGLE([readme],[],[Readme file main part name],[README-dockerhub.md])
# ARG_OPTIONAL_SINGLE([template],[],[Readme appendix template file name],[readme-append.template])
# ARG_OPTIONAL_SINGLE([limit],[],[Max. length of the final README file in bytes (max. 25000)],[25000])
# ARG_OPTIONAL_SINGLE([secrets],[],[File containing secrets (including path relative to PWD)],[../.secrets/secrets-utils.rc])
# ARG_OPTIONAL_SINGLE([gist],[],[Gist ID containing metadata (e.g. badge endpoints) (overrides secrets file)])
# ARG_OPTIONAL_SINGLE([username],[],[Docker Hub user name (overrides secrets file). Only needed for publishing.])
# ARG_OPTIONAL_SINGLE([password],[],[Docker Hub user password (overrides secrets file). Only needed for publishing.])
#
# ARGBASH_GO()
# needed because of Argbash --> m4_ignore([
### START OF CODE GENERATED BY Argbash v2.10.0 one line above ###
# Argbash is a bash code generator used to get arguments parsing right.
# Argbash is FREE SOFTWARE, see https://argbash.io for more info


die()
{
	local _ret="${2:-1}"
	test "${_PRINT_HELP:-no}" = yes && print_help >&2
	echo "$1" >&2
	exit "${_ret}"
}


begins_with_short_option()
{
	local first_option all_short_options='vh'
	first_option="${1:0:1}"
	test "$all_short_options" = "${all_short_options/$first_option/}" && return 1 || return 0
}

# THE DEFAULTS INITIALIZATION - POSITIONALS
_positionals=()
_arg_command=""
# THE DEFAULTS INITIALIZATION - OPTIONALS
_arg_repo=""
_arg_context=""
_arg_readme="README-dockerhub.md"
_arg_template="readme-append.template"
_arg_limit="25000"
_arg_secrets="../.secrets/secrets-utils.rc"
_arg_gist=
_arg_username=
_arg_password=


print_help()
{
	printf '%s\n' "Utility for previewing README files and publishing them on Docker Hub. Assumes to be executed from the current directory."
	printf 'Usage: %s [-v|--version] [-h|--help] [--repo <arg>] [--context <arg>] [--readme <arg>] [--template <arg>] [--limit <arg>] [--secrets <arg>] [--gist <arg>] [--username <arg>] [--password <arg>] [--] [<command>]\n' "$0"
	printf '\t%s\n' "<command>: Command to execute. Available commands:
		 \
# cleanup - Deletes the previously generated README preview and other helper files.
		 \
#           This is the default command if both <command> and <repo> are empty.
		 \
# preview - Generates the README preview file and checks its length.
		 \
#           This is the default command if <command> is empty, but <repo> is not.
		 \
# publish - Generates the README file and publishes it to Docker Hub.
		 \
#           This command must be requested explicitely.
		 \
# (default: '""')"
	printf '\t%s\n' "-v, --version: Prints version"
	printf '\t%s\n' "-h, --help: Prints help"
	printf '\t%s\n' "--repo: Docker Hub repository to publish to (owner/repo) (default: '""')"
	printf '\t%s\n' "--context: Path to the readme files (relative to PWD, e.g. '../docker/xfce') (default: '""')"
	printf '\t%s\n' "--readme: Readme file main part name (default: 'README-dockerhub.md')"
	printf '\t%s\n' "--template: Readme appendix template file name (default: 'readme-append.template')"
	printf '\t%s\n' "--limit: Max. length of the final README file in bytes (max. 25000) (default: '25000')"
	printf '\t%s\n' "--secrets: File containing secrets (including path relative to PWD) (default: '../.secrets/secrets-utils.rc')"
	printf '\t%s\n' "--gist: Gist ID containing metadata (e.g. badge endpoints) (overrides secrets file) (no default)"
	printf '\t%s\n' "--username: Docker Hub user name (overrides secrets file). Only needed for publishing. (no default)"
	printf '\t%s\n' "--password: Docker Hub user password (overrides secrets file). Only needed for publishing. (no default)"
}


parse_commandline()
{
	_positionals_count=0
	while test $# -gt 0
	do
		_key="$1"
		if test "$_key" = '--'
		then
			shift
			test $# -gt 0 || break
			_positionals+=("$@")
			_positionals_count=$((_positionals_count + $#))
			shift $(($# - 1))
			_last_positional="$1"
			break
		fi
		case "$_key" in
			-v|--version)
				echo $0 v21.07.31
				exit 0
				;;
			-v*)
				echo $0 v21.07.31
				exit 0
				;;
			-h|--help)
				print_help
				exit 0
				;;
			-h*)
				print_help
				exit 0
				;;
			--repo)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_repo="$2"
				shift
				;;
			--repo=*)
				_arg_repo="${_key##--repo=}"
				;;
			--context)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_context="$2"
				shift
				;;
			--context=*)
				_arg_context="${_key##--context=}"
				;;
			--readme)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_readme="$2"
				shift
				;;
			--readme=*)
				_arg_readme="${_key##--readme=}"
				;;
			--template)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_template="$2"
				shift
				;;
			--template=*)
				_arg_template="${_key##--template=}"
				;;
			--limit)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_limit="$2"
				shift
				;;
			--limit=*)
				_arg_limit="${_key##--limit=}"
				;;
			--secrets)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_secrets="$2"
				shift
				;;
			--secrets=*)
				_arg_secrets="${_key##--secrets=}"
				;;
			--gist)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_gist="$2"
				shift
				;;
			--gist=*)
				_arg_gist="${_key##--gist=}"
				;;
			--username)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_username="$2"
				shift
				;;
			--username=*)
				_arg_username="${_key##--username=}"
				;;
			--password)
				test $# -lt 2 && die "Missing value for the optional argument '$_key'." 1
				_arg_password="$2"
				shift
				;;
			--password=*)
				_arg_password="${_key##--password=}"
				;;
			*)
				_last_positional="$1"
				_positionals+=("$_last_positional")
				_positionals_count=$((_positionals_count + 1))
				;;
		esac
		shift
	done
}


handle_passed_args_count()
{
	test "${_positionals_count}" -le 1 || _PRINT_HELP=yes die "FATAL ERROR: There were spurious positional arguments --- we expect between 0 and 1, but got ${_positionals_count} (the last one was: '${_last_positional}')." 1
}


assign_positional_args()
{
	local _positional_name _shift_for=$1
	_positional_names="_arg_command "

	shift "$_shift_for"
	for _positional_name in ${_positional_names}
	do
		test $# -gt 0 || break
		eval "$_positional_name=\${1}" || die "Error during argument parsing, possibly an Argbash bug." 1
		shift
	done
}

parse_commandline "$@"
handle_passed_args_count
assign_positional_args 1 "${_positionals[@]}"

# OTHER STUFF GENERATED BY Argbash

### END OF CODE GENERATED BY Argbash (sortof) ### ])
# [ <-- needed because of Argbash

### #####################
### USER CODE BEGINS HERE
### #####################

dump_args() {

    printf "Value of '%s': %s\\n" 'command' "${_arg_command}"
    printf "Value of '%s': %s\\n" 'repo' "${_arg_repo}"
    printf "Value of '%s': %s\\n" 'context' "${_arg_context}"
    printf "Value of '%s': %s\\n" 'readme' "${_arg_readme}"
    printf "Value of '%s': %s\\n" 'template' "${_arg_template}"
    printf "Value of '%s': %s\\n" 'limit' "${_arg_limit}"
    printf "Value of '%s': %s\\n" 'secrets' "${_arg_secrets}"
    printf "Value of '%s': %s\\n" 'gist' "${_arg_gist}"
    printf "Value of '%s': %s\\n" 'username' "${_arg_username}"
    printf "Value of '%s': %s\\n" 'password' "${_arg_password}"
}

do_cleanup() {

	rm -f "${_scrap_append_file}" "${_readme_upload_file}"
	echo "Cleanup finished."
}

do_preview() {

	local repo="${_arg_repo}"
	local readme_context="${_arg_context}"
	local readme_main="${_arg_readme}"
	local readme_append="${_arg_template}"
	local gist="${_arg_gist:-${DEPLOY_GIST_ID}}"

	local readme_append_file
	local readme_main_file
	local repo_name
    local repo_owner

	local -i size
	local -i delta

	if [ -z ${_arg_repo} ] ; then
		die "Repository (owner/repo) must be provided."
	fi

	if [ -z "${readme_context}" ] ; then
		die "Readme context must be provided."
	fi

	if [ -z "${readme_main}" ] ; then
		die "Readme main part file name must be provided."
	fi

    readme_main_file="${readme_context}/${readme_main}"

	if [ ! -f "${readme_main_file}" ] ; then
		die "Missing readme main part file: '${readme_main_file}' "
	fi

	### readme appendix template is optional
	if [ -n "${readme_append}" ] ; then

		readme_append_file="${readme_context}/${readme_append}"

		if [ ! -f "${readme_append_file}" ] ; then
			die "Missing readme appendix template file: '${readme_append_file}' "
		fi

		if [ -z "${gist}" ] ; then
			die "Deployment gist ID must provided through the argument or environment variable!"
		fi
	fi

	### extract the repo owner and its name
    ### alternative 1
    repo_name="$(basename $repo)"
    repo_owner="$(basename $(dirname ${repo}))"
    ### alternative 2
    # repo_name="${repo##*/}"
    # repo_owner="${repo%$repo_name}" ; repo_owner="${repo_owner::-1}" ; repo_owner="${repo_owner##*/}"
    ### also ensure that the deployment repo has only two parts like 'repo_owner/repo_name'
    # _deployment_repo="$(basename $(dirname ${repo}))/$(basename ${repo})"
    _deployment_repo="${repo_owner}/${repo_name}"

	### create the actual README file for Docker Hub by replacing the variables in the append-template (badge links)
    ### and appending it to the main-readme partial file
    ### it is expected that the readme include template contains the following variables:
    ### ${OWNER}    - owner of the deployment repository and also the related deployment gist
    ### ${GIST}     - gist containing the badge endpoints
    ### ${REPO}     - name of the deployment repository

	### readme appendix templating is optional
	if [ -n "${readme_append}" ] ; then

		### replace the environment variables in the template
		( OWNER="${repo_owner}" GIST="${gist}" REPO="${repo_name}" envsubst < "${readme_append_file}" > "${_scrap_append_file}" )

		### append the updated template to the main readme part
		cat "${readme_main_file}" "${_scrap_append_file}" > "${_readme_upload_file}"
		rm -f "${_scrap_append_file}"
	else
		### no appendix and no templating
		cp "${readme_main_file}" "${_readme_upload_file}"
	fi

	echo "Readme preview: ${_readme_upload_file} "

	size=$( wc --bytes "${_readme_upload_file}" | awk '{print $1}' )
	delta=$((_arg_limit-size))
	echo "Readme length is ${size} bytes, leaving ${delta} bytes available (max. ${_arg_limit}). "

	_publishing_obstacle=""
	if [ ${delta} -lt 0 ] ; then
		_publishing_obstacle="Cannot publish. Readme file is $((0-delta)) bytes longer than the limit of ${_arg_limit} bytes."
	fi
}

do_publish() {

	local api_base_url="https://hub.docker.com/v2"
	local user_name="${_arg_username:-${DOCKERHUB_USERNAME}}"
	local user_pwd="${_arg_password:-${DOCKERHUB_PASSWORD}}"

	local response
    local token

	if [ -n "${_publishing_obstacle}" ] ; then
		die "${_publishing_obstacle}"
	fi

	if [ -z "${user_name}" ] ; then
		die "Docker Hub user name must provided through the argument or environment variable!"
	fi
	if [ -z ${user_pwd} ] ; then
		die "Docker Hub user password must provided through the argument or environment variable!"
	fi

	# get token to be able to talk to Docker Hub
	echo "GET token"
	token=$( curl -s \
		-X POST \
		"${api_base_url}/users/login/" \
		-H "Content-Type: application/json" \
		-d '{"username": "'${user_name}'", "password": "'${user_pwd}'"}' | sed -e 's/.*"token": "\(.*\)".*/\1/' )

	echo "UPDATE Docker Hub description using '${_readme_upload_file}'"

	response=$( curl -s \
		-X PATCH \
		"${api_base_url}/repositories/${_deployment_repo}/" \
		-H "Authorization: JWT ${token}" \
		--write-out %{response_code} \
		--output /dev/null \
		--data-urlencode "full_description@${_readme_upload_file}" )

	if [ ${response} -eq 200 ] ; then
		echo "${response}. UPDATE success."
		return 0
	else
		echo "ERROR: ${response}. Unable to UPDATE Docker Hub description."
		echo user_name="${user_name}"
		echo _deployment_repo="${_deployment_repo}"
		echo _readme_upload_file=${_readme_upload_file}
		echo api_base_url=${api_base_url}
		echo "curl -H "Authorization: JWT ${token}" -X PATCH --data-urlencode full_description@${_readme_upload_file} ${api_base_url}/repositories/${_deployment_repo}/"
		curl -v -H "Authorization: JWT ${token}" -X PATCH --data-urlencode full_description@${_readme_upload_file} ${api_base_url}/repositories/${_deployment_repo}/
		return 1
	fi

}

main() {

    ### just debugging support
    # dump_args
	case ${_arg_command,,} in

		cleanup )
			do_cleanup "${_scrap_append_file}" "${_readme_upload_file}"
			;;

		preview )
			do_cleanup "${_scrap_append_file}" "${_readme_upload_file}"
			do_preview
			;;

		publish )
			do_cleanup "${_scrap_append_file}" "${_readme_upload_file}"
			do_preview
			do_publish
			;;

		* )
			if [ -z "${_arg_command}" ] ; then
				if [ -z "${_arg_repo}" ] ; then
					do_cleanup
				else
					do_preview
				fi
			else
				die "Unsupported command '${_arg_command}' "
			fi
			;;
	esac
}

### ################
### MAIN ENTRY POINT
### ################

declare _current_dir
declare _deployment_repo
declare _publishing_obstacle
declare _readme_upload_file
declare _scrap_append_file

_current_dir=$(dirname $0)

_scrap_append_file="${_current_dir}/scrap-append.tmp"
_readme_upload_file="${_current_dir}/scrap-readme.md"

if [ -n "${_arg_secrets}" ] && [ -f "${_arg_secrets}" ] ; then
	# echo "Sourcing secrets..."
	source "${_arg_secrets}"
fi

main

# ] <-- needed because of Argbash
